﻿using System;
using System.Collections.Generic;

namespace KeyWordSearch.TreeParser
{
	internal class AstBuilder
	{
		public static AstNode Build(TokenStream stream)
		{
			AstBuilder bld = new AstBuilder(stream);

			return bld.BuildStatement();
		}

		private Stack<AstNode> NodeStack_;
		private TokenStream TokenStr_;

		private AstBuilder(TokenStream stream)
		{
			if (stream == null)
			{
				throw new ArgumentNullException("stream");
			}


			NodeStack_ = new Stack<AstNode>();

			AstNode node = new AstNode(new Token("EOF", TokenType.EOF, 0, 0, 0, 0));
			NodeStack_.Push(node);

			node = new AstNode(new Token("Root", TokenType.Root, 0, 0, 0, 0));
			NodeStack_.Push(node);

			TokenStr_ = stream;
			TokenStr_.Reset();
		}

		private AstNode BuildStatement()
		{
			TokenStr_.MoveNext();

			BuildExpression();

			return NodeStack_.Peek();
		}

		private void BuildExpression()
		{
			AstNode node;

			BuildTerm();

			while (TokenStr_.Current.Attribute == TokenType.OperatorShortCutedOR)
			{
				node = NodeStack_.Pop();
				NodeStack_.Push(new AstNode(TokenStr_.Current, NodeStack_.Peek()));

				TokenStr_.MoveNext();

				NodeStack_.Peek().AddChilde(node);
				BuildTerm();
				node = NodeStack_.Pop();
				NodeStack_.Peek().AddChilde(node);
			}

			node = NodeStack_.Pop();

			if (NodeStack_.Peek().Count == 0 && NodeStack_.Peek().Value.Attribute == TokenType.Undefined)
			{
				NodeStack_.Pop();
				NodeStack_.Push(node);
			}
			else
			{
				NodeStack_.Peek().AddChilde(node);
			}
		}
		

		private void BuildTerm()
		{
			AstNode node = null;
			BuildFactor();

			while (TokenStr_.Current.Attribute == TokenType.OperatorShortCutedAND)
			{
				node = NodeStack_.Pop();
				NodeStack_.Push(new AstNode(TokenStr_.Current, NodeStack_.Peek()));

				TokenStr_.MoveNext();

				NodeStack_.Peek().AddChilde(node);
				BuildFactor();
				node = NodeStack_.Pop();
				NodeStack_.Peek().AddChilde(node);
			}
		}

		private void BuildFactor()
		{
			AstNode node = null;

			switch (TokenStr_.Current.Attribute)
			{
				case TokenType.LeftBracket:
					TokenStr_.MoveNext();
					NodeStack_.Push(new AstNode(new Token("SubExpression", TokenType.Undefined, TokenStr_.Current.TextElementIndex, 0, TokenStr_.Current.Index, 0)));
					BuildExpression();

					if (TokenStr_.Current.Attribute == TokenType.RightBracket)
					{
						TokenStr_.MoveNext();
					}
					else
					{
						throw new ParseException(")が必用です", TokenStr_.Current);
					}
					break;

				case TokenType.Identifyier:
					BuildFunction();			
					break;

				case TokenType.OperatorNOT:
					NodeStack_.Push(new AstNode(TokenStr_.Current, NodeStack_.Peek()));
					TokenStr_.MoveNext();
					BuildFactor();
					node = NodeStack_.Pop();
					NodeStack_.Peek().AddChilde(node);
					break;

				default:
					throw new ParseException("Factorを作成中にエラーが発生しました", TokenStr_.Current);

			}
		}

		private void BuildFunction()
		{
			NodeStack_.Push(new AstNode(TokenStr_.Current, NodeStack_.Peek()));

			TokenStr_.MoveNext();

			if (TokenStr_.Current.Attribute == TokenType.LeftBracket)
			{
				if (TokenStr_.Peek.Attribute == TokenType.RightBracket)
				{
					TokenStr_.MoveNext();
				}
				else if (TokenStr_.Peek.Attribute == TokenType.Identifyier
					|| TokenStr_.Peek.Attribute == TokenType.StringLiteral)
				{
					TokenStr_.MoveNext();
					BuildArguments();
					if (TokenStr_.Current.Attribute != TokenType.RightBracket)
					{
						throw new ParseException("Functionを作成中にエラーが発生しました", TokenStr_.Current);
					}

				}
				else
				{
					throw new ParseException("Functionを作成中にエラーが発生しました", TokenStr_.Current);
				}
			}
			else
			{
				throw new ParseException("Functionを作成中にエラーが発生しました", TokenStr_.Current);
			}

			if (TokenStr_.Current.Attribute == TokenType.RightBracket)
			{
				TokenStr_.MoveNext();
				//AstNode node = NodeStack_.Pop();
				//NodeStack_.Peek().AddChilde(node);
			}
			else
			{
				throw new ParseException("Function作成中にエラーが発生しました", TokenStr_.Current);
			}
		}

		private void BuildArguments()
		{
			BuildArgument();

			while(TokenStr_.Current.Attribute == TokenType.Comma)
			{
				TokenStr_.MoveNext();
				BuildArgument();
			}
		}

		private void BuildArgument()
		{
			AstNode node=null;

			switch (TokenStr_.Current.Attribute)
			{
				case TokenType.StringLiteral:
					node = new AstNode(TokenStr_.Current);
					NodeStack_.Peek().AddChilde(node);
					TokenStr_.MoveNext();
					break;

				case TokenType.Identifyier:
					BuildComplexArgument();
					break;

				default:
					throw new ParseException("Argumentを作成中にエラーが発生しました", TokenStr_.Current);
			}

		}

		private void BuildComplexArgument()
		{
			AstNode node = null;
			NodeStack_.Push(new AstNode(TokenStr_.Current));

			switch (TokenStr_.Peek.Attribute)
			{
				case TokenType.OperatorOR:
					node = NodeStack_.Pop();
					TokenStr_.MoveNext();
					NodeStack_.Push(new AstNode(TokenStr_.Current));
					TokenStr_.MoveNext();
					NodeStack_.Peek().AddChilde(node);
					BuildComplexArgument();
					break;

				case TokenType.Comma:
					TokenStr_.MoveNext();
					break;

				case TokenType.RightBracket:
					TokenStr_.MoveNext();
					break;

				default:
					throw new ParseException("Argumentの作成中にエラーが発生しました", TokenStr_.Peek);
			}

			node = NodeStack_.Pop();
			NodeStack_.Peek().AddChilde(node);
		}
	}
}
